package com.cxb.springboot.service;

import com.alibaba.fastjson.JSON;
import com.cxb.springboot.domin.ZkData;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

import static com.cxb.springboot.controller.ZkController.HRHK_ZT;

/**
 * @Classname ZkService
 * @Date 2023/4/2 22:55
 * @Created by Administrator
 * @Description TODO
 */
@Service
@Slf4j
public class ZkService {

    @Autowired
    private CuratorFramework curatorClient;

    private static final int LOCK_TIME = 15;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 删除指定路径的节点信息
     *
     * @param path
     */
    public void deleteNode(String path) {
        if (checkLock(path)) {
            try {
                curatorClient.delete().guaranteed();
                curatorClient.delete().forPath(path);
            } catch (Exception e) {
                log.error("节点释放异常，请检查: {}", path);
            }
        } else {
            log.error("该节点不存在，无需删除: {}", path);
        }
    }

    /**
     * 检查锁
     *
     * @param path
     * @return
     */
    public boolean checkLock(String path) {
        Stat stat = null;
        try {
            stat = curatorClient.checkExists().forPath(path);
        } catch (Exception e) {
            log.error("检查节点异常:", e);
        }
        return stat != null;
    }

    /**
     * 存数据
     *
     * @param path
     * @param obj
     * @param <T>
     * @throws Exception
     */
    public <T> void storageData(String path, T obj) throws Exception {
        byte[] bytes = JSON.toJSONBytes(obj);
        Stat stat = curatorClient.checkExists().forPath(path);
        if (stat != null) {
            curatorClient.setData().forPath(path, bytes);
            return;
        }
        curatorClient.create().creatingParentsIfNeeded()
                .withMode(CreateMode.PERSISTENT).forPath(path, bytes);
    }

    /**
     * 更新数据
     *
     * @param path
     * @param zkData
     */
    public void updateData(String path, ZkData zkData) {
        InterProcessMutex lock = new InterProcessMutex(curatorClient, path);
        try {
            log.info("{}尝试获取锁...", path);
            if(lock.acquire(LOCK_TIME, TimeUnit.SECONDS)) {
                log.info("{}获取锁成功...", path);
                ZkData readData = readZkData(path);
                BeanUtils.copyProperties(zkData, readData);
                storageData(getPath(zkData.getCode()), readData);
                log.info("{}更新数据成功...", path);
            } else {
                log.error("获取锁失败...");
            }
        } catch (Exception e) {
            log.error("更新数据异常：", e);
        } finally {
            try {
                if (lock.isAcquiredInThisProcess()) {
                    lock.release();
                    log.info("{}释放锁成功...", path);
                }
            } catch (Exception e) {
                log.error("释放锁异常：", e);
            }
        }

    }

    /**
     * 读取数据
     *
     * @param path
     * @param objType
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T readData(String path, Class<T> objType) throws Exception {
        byte[] bytes = "".getBytes();
        Stat stat = curatorClient.checkExists().forPath(path);
        if (stat != null) {
            // 1、通过使用.storingStatIn()方法，你可以获取到节点的最新状态信息，包括版本号、创建时间等。这样可以确保你读取到的是最新的数据。
//             bytes = curatorClient.getData().storingStatIn(stat).forPath(path);
            // 2、通过使用client.sync()方法，你可以确保在读取数据之前，先同步ZooKeeper服务器的状态，以获取最新的数据。
//            curatorClient.sync().forPath(path);
            bytes = curatorClient.getData().forPath(path);
        }
        return JSON.parseObject(bytes, objType);
    }

    public ZkData readZkData(String path) throws Exception {
        int i = 0;
        ZkData zkData = null;
        while (i < 1000) {
            Thread.sleep(10);
            zkData = readData(path, ZkData.class);
            log.info("{}读取的数据:{}", path, objectMapper.writeValueAsString(zkData));
            i ++;
        }
        log.info("读取数据完成！！！");
        return zkData;
    }

    /**
     * 查询指定code的zk的数据
     *
     * @param code
     * @return
     */
    public String getPath(String code) {
        return HRHK_ZT + code;
    }

}
